/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fjsei.yewu.inspect;

import com.alibaba.fastjson2.JSON;
import com.querydsl.core.BooleanBuilder;
import graphql.relay.Connection;
import graphql.schema.DataFetchingEnvironment;
import jakarta.persistence.criteria.*;
import md.cm.base.Companies;
import md.cm.geography.Adminunit;
import md.cm.geography.Village;
import md.cm.unit.Division;
import md.cm.unit.Unit;
import md.specialEqp.*;
import md.specialEqp.inspect.*;
import md.specialEqp.type.*;
import md.system.User;
import org.fjsei.yewu.entity.fjtj.DictArea;
import org.fjsei.yewu.entity.fjtj.PipeLineUnit;
import org.fjsei.yewu.entity.fjtj.UntMge;
import org.fjsei.yewu.graphql.DbPageConnection;
import org.fjsei.yewu.graphql.IdMapper;
import org.fjsei.yewu.input.DeviceCommonInput;
import org.fjsei.yewu.input.IspCommonInput;
import org.fjsei.yewu.jpa.PageOffsetFirst;
import org.fjsei.yewu.pojo.jsn.*;
import org.fjsei.yewu.pojo.sei.IspTzFieldSnapshot;
import org.fjsei.yewu.resolver.Comngrql;
import org.fjsei.yewu.util.Tool;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.execution.BatchLoaderRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static md.specialEqp.BusinessCat_Enum.MANUFACT;
import static org.fjsei.yewu.util.Tool.F2String;
import static org.fjsei.yewu.util.Tool.St2String;

/**默认Unit模型的属性； graphQL接口;
 * 底下批处理方案还不是一种的。 注释掉的也是1个方案。
 * 每一个graphQL的Object对象type都需单独声明一个XxxController implements IdMapper<type> {} 否则Id都无法转换成Relay要的GlobalID的。
 * */

@Controller
public class DetailController extends Comngrql implements IdMapper<Detail> {
	@PersistenceContext(unitName = "entityManagerFactorySei")
	private EntityManager emSei;
	//private final Units units;
	private final Companies companies;
	private final TaskRepository tasks;
	private final IspRepository isps;
	//private final ApprovalStmRepository  approvalStms;
	private final ReportRepository  reports;
	private final DetailRepository  details;
	@Autowired
	private PipingUnitRepository pipingUnitRepository;

	/** registry参数：IDEA报红却能运行成功，实际运行注入DefaultBatchLoaderRegistry;
     *  消除报红，测试平台Units没有报红 https://blog.csdn.net/zy103118/article/details/84523452
	 * 添加了 @ComponentScan({"md","org.fjsei.yewu"}) 以后: Units报红消除了。
	 * */
	public DetailController(BatchLoaderRegistry registry, Companies companies, TaskRepository tasks, IspRepository isps, ReportRepository reports, DetailRepository details) {
		this.companies = companies;
		this.tasks = tasks;
		this.isps = isps;
		this.reports = reports;
		this.details = details;
	}

	@Transactional(propagation= Propagation.REQUIRES_NEW)
	public String abandonDetailInner(Isp isp) {
		/* Isp不管了,？有些确实需要删除的, todo://根据Isp的状态判定，已经终结的报告Isp不能删除。
		 */
		int errCnt=0;
		if(null!=isp){
			//	isp.setBus(null);		//不能省掉 报错。
			//已经关联的Report的清理？
			if(!isp.getReps().isEmpty()) errCnt++;
			//   Assert.isTrue(isp.getReps().isEmpty(),"关联Report未清理");
			//特别的字段@ManyToMany需要清理掉： ispMen 解除关系
			//Set<User> mens= isp.getIspMen();
			//mens.forEach(a -> a.getIsp().remove(isp));
			//若不解除关系不能立即给正确的应答，在缓存期限时间内做关联查找会报错某关联id找不到。
			//下面这两行若去掉一个都会导致关联id找不到，除非cache缓存时间过了才行，或者其他操作影响。
			//isp.getTask().getIsps().remove(isp);
			//emSei.remove(isp);
			isps.delete(isp);        //应该是其他表关联了Isp,删除需要检查全部被对方eqp关联用到的情况，eqp表却对关联id字段没有做索引，导致巨慢！。
			//emSei.flush(); 加了更慢！
		}
		return  0==errCnt? "":"有1个关联的Report未清理";
	}

	/**删除某一个Detail检验业务记录
	 * 有些无效操作竟然没有报错的。
	 * 冷数据Isp相关尽量不作删除。相关性对象的关联拆除或者删除更深关联层次的。
	 * 协议申请拟定过程可以生成临时的任务Detail业务，实现提前的计费。
	 * */
	@MutationMapping
	@Transactional
	public boolean abandonDetail(@Argument String detId,@Argument String reason) {
		Detail detail=entityOf(detId,Detail.class);
		Assert.isTrue(detail != null,"未找到Detail:"+detId);
		Isp isp=detail.getIsp();
		detail.setIsp(null);    //切断关联isp-detail: 加速？
//		 Isp不管了,？有些确实需要删除的,
//		todo://根据Isp的状态判定，已经终结的报告Isp不能删除。

		String resp="";
		if(null!=isp){
			resp= abandonDetailInner(isp);
		}
		//清理关联的Charging? 收费计算信息清理。对方表有我放的ID存储着，报错！不能简单删除关联id;【麻烦】一个连接着另外一个id;连串清理。
		//收费Charging的清理 关联Charging.pipus<>
		detail.getFees().forEach(a -> {
			List<PipingUnit> pipingUnits=a.getPipus();
			Assert.isTrue(pipingUnits.isEmpty(),"关联Charging.pipus未清理");
			//多对多@ManyToMany反而更容易清理：可自动删除掉中间表数据。
			emSei.remove(a);
		} );
		//被引用的：管道单元锁要清除 pipus，不然就报错了。
//		detail.getPipus().forEach(a -> {
//			a.setDet(null);
//		} );
		emSei.remove(detail);
		//缓存List<>报错:Unable to find md.specialEqp.inspect.Detail with id 26527f3b-5af7-4966-9b33-4eb7d6154e76,增加内存对象同步处置：
		detail.getTask().getDets().remove(detail);
		return  resp.length()==0;
	}

	/**当前我的检验业务： 不上ES搜索，查关系数据库，又没搞对索引：太慢了！
	 * 【综合考量应该怎么加索引】还没规划设置好索引，本查询特别的慢！很多查询语句也并不是都需要那么多的字段，Entity Projection没用上。不同语句使用的频率，权衡代价和获益。
	 * 链式改造：部分GPT生成的，有待核对。
	 * */
	@QueryMapping
	public Connection<Detail> findMeDetails(@Argument String orderBy, @Argument Boolean asc, @Argument IspCommonInput where, @Argument Integer first,
											@Argument String after, @Argument Integer last, @Argument String before, DataFetchingEnvironment env) {
		User user= checkAuth();
		if(user==null)   return null;
		DbPageConnection<Detail> connection=new DbPageConnection(env);
		int offset=connection.getOffset();
		int limit=connection.getLimit();
		Pageable pageable;
		if (!StringUtils.hasLength(orderBy))
			pageable = PageOffsetFirst.of(offset, limit);
		else
			pageable = PageOffsetFirst.of(offset, limit, Sort.by(asc ? Sort.Direction.ASC : Sort.Direction.DESC, orderBy));
		QDetail qm = QDetail.detail;
		BooleanBuilder builder = new BooleanBuilder();
		BooleanBuilder userbuild = new BooleanBuilder();
		//默认：在这个位置qm.isp.report.stm就会出现null 空指针(IDEA观察到)，但可能也根本就没提示的  直接歇菜。querydsl解决办法@QueryInit("report.stm.master")多对一一对一的字段才会出现此类问题。
		userbuild.or(qm.isp.report.stm.master.eq(user)).or(qm.isp.report.stm.authr.contains(user));
		BooleanBuilder stabuild = new BooleanBuilder();
		stabuild.or(qm.isp.report.stm.sta.eq(Procedure_Enum.BEGIN)).or(qm.isp.report.stm.sta.eq(Procedure_Enum.MAKE));
		builder.and(userbuild).and(stabuild);
		if(StringUtils.hasText(where.getServu())) {
			Unit unit=fromInputUnitGlobalID(where.getServu());
			Assert.notNull(unit,"未找到Unit:"+where.getServu());
			builder.and(qm.task.servu.id.eq(unit.getId()));
		}
		if(StringUtils.hasText(where.getCod()))
			builder.and(qm.isp.dev.cod.containsIgnoreCase(where.getCod()));
		if(StringUtils.hasText(where.getOid()))
			builder.and(qm.isp.dev.oid.containsIgnoreCase(where.getOid()));
		if(StringUtils.hasText(where.getIdent()))
			builder.and(qm.ident.containsIgnoreCase(where.getIdent()));
		if(StringUtils.hasText(where.getNo()))
			builder.and(qm.isp.no.containsIgnoreCase(where.getNo()));
		//【神奇了 querydsl】实际上这builder在这直接照抄findMeIsps(){}里面的竟然也可以！ 会从QIsp QDetail自动生成关联关系的。我都没定义QDetail的必要了。
		Slice<Detail> rpage= (Slice<Detail>)details.findAll(builder,pageable);
		List<Detail> isps=(List<Detail>) rpage.toList();
		return connection.setListData(isps).get(env);
	}
	public List<PipingUnit> listAllPipingUnit_多单元测1(@Argument String detId, @Argument String orderBy, @Argument Boolean asc) {
		Detail detail = entityOf(detId,Detail.class);
		Assert.notNull(detail,"未找到Detail:"+detId);
		Hibernate.initialize(detail.getIsp());
		Hibernate.initialize(detail.getIsp().getDev());  // 关键步骤
		Object unproxyEqp = Hibernate.unproxy(detail.getIsp().getDev());
		Pipeline pipe = (Pipeline) unproxyEqp;  // 现在可以安全转换
		if(pipe.getCells().size()<20)
		{
			List<PipingUnit> batch=new ArrayList<>();
			PipingUnit sam= pipe.getCells().get(10);
			for(int i=0; i<500; i++){
				PipingUnit unit = PipingUnit.builder()
						.pipe(pipe)
						.code("FAK-E-590U"+i)
						.ust(sam.getUst())
						.reg(sam.getReg())
						.dets(List.of(detail))
						.svp(sam.getSvp())
						.start(sam.getStart())
						.stop(sam.getStop())
						.rno(sam.getRno())
						.dia(sam.getDia())
						.leng(sam.getLeng())
						.crDate(sam.getCrDate())
						.build();
				batch.add(unit);
				pipe.getCells().add(unit);
			}
			pipingUnitRepository.saveAll(batch);
		}
		return detail.getPipus();
	}
	public List<PipingUnit> listAllPipingUnit_多单元测2(@Argument String detId, @Argument String orderBy, @Argument Boolean asc) {
		Detail detail = entityOf(detId,Detail.class);
		Assert.notNull(detail,"未找到Detail:"+detId);
		List<PipingUnit> linkpips=detail.getPipus();
		Object unproxyEqp = Hibernate.unproxy(detail.getIsp().getDev());
		Pipeline pipe = (Pipeline) unproxyEqp;  // 现在可以安全转换
		if(linkpips.size()<20)
		{
			List<PipingUnit> batch=new ArrayList<>();
			List<PipingUnit>  cells= pipe.getCells();
			for(int i=0; i<cells.size(); i++){
				linkpips.add(cells.get(i));
			}
			detail.setPipus(linkpips);
			details.save(detail);
		}
		return detail.getPipus();
	}
	@QueryMapping
	//orderBy 可支持直接指定某属性的下级字段。 {"orderBy": "pos.building",}
	public List<PipingUnit> listAllPipingUnit(@Argument String detId, @Argument String orderBy, @Argument Boolean asc) {
		Detail detail = entityOf(detId,Detail.class);
		Assert.notNull(detail,"未找到Detail:"+detId);
		return detail.getPipus();
	}

	/**报告Isp：所相关的基础信息赋值： 正常的分项报告和其isp的主报告公用的基础数据快照。正常报告编制时刻只需要快照运行一趟的，除非有些新的变动。
	 * 影响范围：当前Isp的报告的snapshot字段{}填最新的台账信息。分项报告同样一起变。
	 * 返回 主报告；在后端做这件事可不可篡改基础数据给报告去显示，若是放在前端自己提取和保存会引起篡改随意性较大，改完了和监察以及本后端不一致。
	 * 前端想从rep.isp.servu直接注入给编辑器选择列表组件，不妥，还是走orc/snapshot{}，前端参数集中注入orc避免多个入口增加rep，动态性质rep。
	 * */
	@MutationMapping
	@Transactional
	public Report fillReportTzFields(@Argument("detId") String ispId) {
//		Detail detail=entityOf(detId,Detail.class);
		Isp isp=entityOf(ispId,Isp.class);
		Assert.notNull(isp,"未找到Isp:"+ispId);
		Report report =isp.getReport();
		Assert.notNull(report,"未找到主报告,isp="+isp.getId());
		Detail  bus=isp.getBus();
		BusinessCat_Enum bsType=isp.getBsType();
		Assert.notNull(bus,"未配套Detail:"+ispId+",历史报告吗");		//Detail可能超长时间后被删除掉。
		Task task=bus.getTask();
		Assert.notNull(task,"未配套Task:"+ispId+",历史报告吗");
		IspTzFieldSnapshot snapshot = new IspTzFieldSnapshot();
		//没有设备台账的情形： Detail里面的定位信息字段 ident 导入？
		Unit servu = null!=isp.getServu()? isp.getServu() : task.getServu();
		snapshot.set服务单位(null!=servu? servu.name() :null);
		if(null==isp.getDev()) {
			//"F" 安全阀，没关联台帐业务
			if("F".equals(bus.getType())){					//Float: 录入整数的转成字符串 自动加“.0”给前端了:前端会奇怪。
				snapshot.set公称压力(F2String(bus.getOpprn())).set公称通径(F2String(bus.getDiame()));
			}
			if(MANUFACT.equals(bsType) && ("2".equals(bus.getType()) || "1".equals(bus.getType()) ) ){
				snapshot.set制造单位(null!=servu? servu.name() :null).set设备类别(bus.getSort());
			}
			String strJson = JSON.toJSONString(snapshot);            //"{}"
			report.setSnapshot(strJson);
			if(!StringUtils.hasText(report.getData())) report.setData("{}");
			reports.save(report);
			return report;        //没挂接设备台账的独立报告。
		}
		Object unproxiedEntity = Hibernate.unproxy(isp.getDev());
		Eqp eqp= (Eqp) unproxiedEntity;
		DeviceComnPam pam= JSON.parseObject(eqp.getPa(), ElevatorPam.class);
		String 进口类型=switch (eqp.getImpt()) {case 2 -> "部件进口"; case 3 -> "整机进口"; default -> "国产";};
		//规范代码 替换汉语名称，前端自己映射转码显示中文名？ 方便抽取报告 统计？
		snapshot.setEqpcod(eqp.getCod()).set监察识别码(eqp.getOid()).set使用证号(eqp.getCert()).set设备代码(eqp.getSno()).set设备类别(eqp.getSort())
				.set出厂编号(eqp.getFno()).set型号(eqp.getModel()).set设备名称(eqp.getTitl()).set设备品种(eqp.getVart()).set子设备品种(eqp.getSubv())    //品种  EQP_VART_NAME
				.set单位内部编号(eqp.getPlno()).set制造日期(null!=eqp.getMkd()? eqp.getMkd().toString() : "").set下检日期(null!=eqp.getNxtd2()? eqp.getNxtd2().toString() : "")
				.set设备使用地点(eqp.getAddress()).set投用日期(null!=eqp.getUsed()? eqp.getUsed().toString() : "").set检验类别(isp.getBsType().getName())
				.set设备联系手机(eqp.getLpho()).set设备联系人(pam.get联系人()).set进口类型(进口类型);
		Village village = eqp.getVlg();
		if (null != village) {
			snapshot.set楼盘(village.getName()).set楼盘地址(village.getAddress()).set楼盘性质(village.getType());     //前面行政地理描述部分要省略掉。
		}
		if(null!=eqp.getAd()){
			snapshot.set使用地区域(eqp.getAd().getName());
		}
		UntMge untMge = null;
		Unit mtu = eqp.getMtu();
		if (null != mtu)
			snapshot.set维保单位(mtu.name());
		if (null != eqp.getRemu())
			snapshot.set改造单位(eqp.getRemu().name());
		if (null != eqp.getMakeu()) {
			snapshot.set制造单位(eqp.getMakeu().name()).set制造单位地址(eqp.getMakeu().getAddress(null));
		}
		if (null != eqp.getRepu())
			snapshot.set维修单位(eqp.getRepu().name());
		Division division = eqp.getUsud();
		if (null != division) {
			//.SECUDEPT_ID	'分支机构ID' || .SAFE_DEPT_ID '安全管理部门' mtp=2 :1?
			snapshot.set分支机构(division.getName()).set分支机构地址(division.getAddress()).set使管部类型(division.getBranch()?"内设分支机构":"内设部门");     //不要area也就是lare所对应的区划地址前缀；
		}else
			snapshot.set使管部类型("无内设");
		Unit useu = eqp.getUseu();
		if (null != useu) {
			Adminunit ad= null!=useu.getCompany()? useu.getCompany().getAd() : useu.getPerson().getAd();
			snapshot.set使用单位(useu.name()).set使用单位地址(useu.getAddress(null)).set使用单位信用码(useu.getNo(null))
					.set使用单位邮编(null!=ad? ad.getZipcode() : "")                 //graphQL接口 getAddress
				.set使用单位电话(useu.getPhone(null)).set单位联系人(useu.getLinkMen(null));
		}
		if (null != eqp.getInsu())
			snapshot.set安装单位(eqp.getInsu().name());
		Unit owner = eqp.getOwner();
		if (null != owner)
			snapshot.set产权单位(owner.name()).set产权单位地址(owner.getAddress(null));
		//和特种设备类型相关的部分：不同业务类型的都一样带上这些参数值？
		//Class<?> devtype=report.getIsp().getDev().getClass();   不是Elevator而是Hibernate代理类。
		if(unproxiedEntity instanceof Elevator) {            //电梯的
			Elevator device = (Elevator) unproxiedEntity;
			snapshot.set控制方式(device.getOpm()).set电梯层数(null!=device.getFlo()? device.getFlo().longValue() :0);
			String oldsvp = eqp.getSvp();
			ElevatorSvp svp = JSON.parseObject(oldsvp, ElevatorSvp.class);
			ElevatorPam pa= JSON.parseObject(device.getPa(), ElevatorPam.class);
			//运行速度 不能用(device.getVl().doubleValue())太多位数了。
			snapshot.set电梯站数(null != svp.get电梯站数() ? svp.get电梯站数().longValue() : 0).set电梯门数(null != svp.get电梯门数() ? svp.get电梯门数().longValue() : 0)
					.set运行速度(device.getVl()).set额定载荷(device.getRtl()).set注册代码(device.getRcod()).set名义宽度(String.valueOf(device.getWesc()))
					.set倾斜角度(svp.get倾斜角度()).set提升高度(String.valueOf(device.getHlf())).set使用区长度(F2String(device.getLesc())).set限速器型号(device.getLimm())
					.set限速器编号(pa.get限速器号()).set限速上电气速(pa.get上限电速()).set限速上机械速(pa.get上限机速()).set限速下电气速(pa.get下限电速()).set限速下机械速(pa.get下限机速())
					.set对限速型号(pa.get对限速型()).set对限速编号(pa.get对限速号()).set开门方式(device.getDoop()).set控制屏型号(device.getCpm()).set控制屏编号(svp.get屏号())
					.set曳引机型号(device.getTm()).set曳引机编号(svp.get曳引号()).set电动机型号(device.getMtm()).set层门型号(pa.get层门型号()).set上行保护型号(svp.get上护型号())
					.set意外保护型号(device.getProt()).set缓冲器型号(pa.get缓型号()).set缓冲器形式(device.getBuff()).set对重块数量(svp.get对重块数()).set轿厢装修(pa.get装修()).set主机型号(device.getMtm())
					.set主机编号(svp.get主机号()).set钢丝绳直径(svp.get悬挂绳径()).set顶升形式(svp.get顶升形式()).set油缸数量(svp.get油缸数()).set油缸形式(svp.get油缸形式())
					.set液泵型号(pa.get泵型号()).set液泵编号(pa.get泵编号()).set液泵流量(pa.get泵流量()).set液泵功率(svp.get泵功率()).set防爆标志(svp.get防爆标志()).set防爆等级(svp.get区域防爆())
					.set附加装置(device.getAap()).set是公共交通(svp.get公共交通()).set额定速度上(pa.get上行额速()).set额定速度下(pa.get下额定速());
		} else if(unproxiedEntity instanceof Vessel) {             //容器的
			snapshot.set上路号牌(eqp.getPlat());
			Vessel device = (Vessel) unproxiedEntity;
			VesselSvp svp= JSON.parseObject(device.getSvp(), VesselSvp.class);
			VesselPam pa= JSON.parseObject(device.getPa(), VesselPam.class);
			snapshot.set设计年限(svp.get年限()).set主体结构(svp.get结构()).set运行状态(pa.get运行态()).set设计压力(device.getPrs()).set许工作温(svp.get许工作温()).set内径(svp.get内径())
					.set是换热(svp.get是换热()).set容积换热(device.getVol()).set设计单位(null != svp.get设计单位() ? svp.get设计单位().getName() : null).set产品标准(svp.get产品标准())
					.set设计日期(Tool.toString(svp.get设计日期())).set设计图号(svp.get设计图号()).set安装型式(device.getMont()).set保温绝热方式(Tool.String(device.getInsul()))
					.set支座型式(svp.get支座()).set外形高长(device.getHighs()).set罐外形长(F2String(svp.get罐外长())).set允许充装量(null != pa.get充重() ? pa.get充重().toString() : null)
					.set压力表量程(pa.get表量程()).set压力表精度(pa.get表精度()).set壳程设计压力(svp.get壳设压()).set壳程使用压力(pa.get壳用压()).set管程设计压力(null != svp.get管设压() ? svp.get管设压().toString() : null)
					.set管程使用压力(pa.get管用压()).set夹套设计压力(svp.get夹设压()).set夹套使用压力(pa.get夹用压()).set壳程设计温度(svp.get壳设温()).set壳程使用温度(pa.get壳用温())
					.set管程设计温度(svp.get管设温()).set管程使用温度(pa.get管用温()).set夹套设计温度(svp.get夹设温()).set夹套使用温度(pa.get夹用温()).set筒体腐裕度(pa.get筒腐裕())
					.set封头腐裕度(pa.get头腐裕()).set壳程介质(svp.get壳介()).set夹套介质(device.getJakm()).set管程介质(svp.get管介()).set筒体材质(pa.get筒料()).set封头材质(pa.get封料())
					.set夹套材质(pa.get夹料()).set衬里材质(pa.get衬料()).set筒体厚度(svp.get筒厚()).set封头厚度(svp.get头厚()).set夹套厚度(svp.get夹厚()).set衬里厚度(svp.get衬厚())
					.set制造国(svp.get制造国()).set设计规范(svp.get设规范()).set公称壁厚(svp.get壁厚()).set设计介质(svp.get设计介()).set工作压力(pa.get工作压()).set工作温度(pa.get工作温())
					.set工作介质(pa.get工作介()).set外筒体材质(svp.get罐材外()).set外筒体厚度(svp.get罐厚外筒()).set外封头厚度(svp.get罐厚外头()).set腐蚀裕量(pa.get腐裕()).set运输证号(pa.get运输证())
					.set车辆类型(svp.get车辆类()).set车架号(svp.get车架()).set外形尺寸(pa.get外尺寸()).set额定载荷(F2String(device.getRtlf())).set盛装介质(pa.get介质()).set氧舱人数(svp.get舱容限())
					.set容器规格(svp.get规格()).set许工作介质(svp.get许工作介()).set许工作压力(svp.get许工作压()).set舱体设计压(F2String(svp.get舱设压())).set人均舱容(svp.get人均());
		}else if(unproxiedEntity instanceof Crane) {
			snapshot.set上路号牌(eqp.getPlat());
			Crane device = (Crane) unproxiedEntity;
			CraneSvp svp= JSON.parseObject(device.getSvp(), CraneSvp.class);
			CranePam pa= JSON.parseObject(device.getPa(), CranePam.class);
			snapshot.set额定载荷(F2String(device.getRtlf())).set额定起重量(device.getCap()).set生产率(svp.get生产率()).set工作级别(device.getJobl()).set起升速(svp.get起升速())
					.set跨度(null != device.getSpan() ? String.valueOf(device.getSpan()) : null).set速度(device.getRvl()).set小车速度(device.getScv()).set额定速度(device.getVls())
					.set提升高度(F2String(device.getHlf())).set电梯层数(null != device.getFlo() ? device.getFlo().longValue() : 0).set电梯站数(null != device.getNs() ? device.getNs().longValue() : 0)
					.set电梯门数(null != pa.get门数() ? Long.valueOf(pa.get门数().longValue()) : 0).set防爆标志(svp.get防爆()).set附属装置名称(svp.get附装名()).set易燃易爆物(pa.get易爆物())
					.set场所防爆级(pa.get所爆级()).set主升1额起量(pa.get钩1重()).set主升2额起量(pa.get钩2重())
					.set副升1额起量(null != pa.get副钩1重() ? pa.get副钩1重().toString() : null).set副升2额起量(pa.get副钩2重()).set停车起升式(svp.get车方法())
					.set泊位数量(null != device.getPnum() ? device.getPnum().toString() : null).set额升降速度(svp.get额起升速()).set防爆等级(svp.get防爆级())
					.set最大进出时(null != device.getMiot() ? device.getMiot().toString() : null).set适停车质量(device.getPcw()).set适停尺寸(device.getPcs())
					.set起升副额起(pa.get副额起()).set大车速度(device.getCvl()).set悬臂长1(svp.get悬长()).set悬臂长2(null != svp.get悬2长() ? svp.get悬2长().toString() : null)
					.set起重力矩(device.getMom()).set回转速度(device.getRtv()).set动力系统(pa.get动力系统()).set变幅速度(device.getLuff()).set最大工作幅(svp.get最工幅())
					.set最小工作幅(pa.get最小幅()).set有监控(svp.get有监控()).set独立高度(F2String(pa.get独立高())).set变幅形式(device.getLuf()).set移动类型(pa.get移动型())
					.set臂架铰点高(F2String(pa.get架铰高())).set检工作幅度(F2String(pa.get工作幅())).set导梁支承跨度(pa.get导支跨度()).set额定承载量(svp.get架桥承())
					.set主升吊具型(svp.get主吊具()).set检验时吊具(pa.get检吊具()).set主起悬挂部件(svp.get主悬件()).set监控系统生产单(pa.get监系单()).set监控系统编号(pa.get监系号())
					.set监控系统型号(pa.get监系型()).set下降深度(pa.get深度()).set导轨架高度(F2String(pa.get轨架高())).set传动方式(svp.get传动()).set检对重数量(pa.get对块数检())
					.set检附墙架数(pa.get附墙架()).set防坠安器型(pa.get坠安型()).set防坠安器编(pa.get坠安号()).set安全钳型号(pa.get钳型号()).set安全钳编号(pa.get钳编号())
					.set限速器型号(svp.get限速器型()).set限速器编号(pa.get限速器号()).set工作方式(svp.get工作式()).set使用环境(pa.get环境()).set吊笼数量(svp.get笼数())
					.set开关门驱动式(svp.get门驱动());
		}
		else if(unproxiedEntity instanceof FactoryVehicle) {
			snapshot.set上路号牌(eqp.getPlat());
			FactoryVehicle device = (FactoryVehicle) unproxiedEntity;
			FactoryVehicleSvp svp= JSON.parseObject(device.getSvp(), FactoryVehicleSvp.class);
			FactoryVehiclePam pa= JSON.parseObject(device.getPa(), FactoryVehiclePam.class);
			snapshot.set底盘编号(svp.get底盘号()).set发动机号(svp.get机编号()).set自重量(pa.get车空重()).set防爆等级(svp.get防爆级()).set传动方式(svp.get传动()).set动力方式(device.getPow())
					.set最大升高(String.valueOf(svp.get升最高())).set运行速度(svp.get时速()).set额定载荷(F2String(device.getRtlf())).set主机功率(svp.get机功率())
					.set最大行驶坡度(svp.get最坡度()).set最大行驶速度(svp.get时速()).set后轮轮距(svp.get后轮距()).set前轮轮距(svp.get前轮距())
					.set车厢数(svp.get厢数()).set车厢座位数(pa.get厢座位()).set车头座位数(pa.get头座位());
		}
		else if(unproxiedEntity instanceof Pipeline) {
			Pipeline device = (Pipeline) unproxiedEntity;
			PipelineSvp svp= JSON.parseObject(device.getSvp(), PipelineSvp.class);
			PipelinePam pa= JSON.parseObject(device.getPa(), PipelinePam.class);
			snapshot.set管道设备名(svp.get管道设备名()).set设备等级(device.getLevel()).set注册代码(device.getRcod());
		}
		else if(unproxiedEntity instanceof Boiler) {
			Boiler device = (Boiler) unproxiedEntity;
			BoilerSvp svp= JSON.parseObject(device.getSvp(), BoilerSvp.class);
			BoilerPam pa= JSON.parseObject(device.getPa(), BoilerPam.class);
			snapshot.set加热方式(svp.get加热方式()).set燃料种类(device.getFuel()).set额定蒸发量(null != device.getPower() ? device.getPower().toString() : null).set额定温度(svp.get许工温())
					.set额定压力(null != device.getPres() ? device.getPres().toString() : null).set工作压力(pa.get使用压力()).set出口温度(svp.get设出口温()).set回流温度(pa.get回水温())
					.set介质名称(svp.get介名()).set介质牌号(svp.get介牌号()).set介质化验日(pa.get介验日()).set耐压试验压(F2String(pa.get试压力()))
					.set耐压试验日(pa.get压验日()).set再热蒸汽流量(F2String(pa.get再汽流())).set锅筒工作压力(pa.get筒工作压()).set过热器出口压(pa.get过热压()).set过热器出口温(pa.get过热温())
					.set再热出口压力(F2String(pa.get再热出压())).set再热出口温度(F2String(pa.get再热出温())).set再热入口压力(F2String(pa.get再热入压()))
					.set再热入口温度(F2String(pa.get再热入温())).set给水压力(pa.get给水压()).set给水温度(pa.get给水温()).set设计出口压力(svp.get设出口压()).set额定出力(pa.get额定出力())
					.set介质出口温度(pa.get介出温()).set燃烧方式(device.getBmod()).set燃烧设备(svp.get烧设备()).set工作介质(svp.get工作介质()).set水循环式(svp.get水循环()).set水处理式(svp.get水处式())
					.set有过热器(pa.get有过热器()).set蒸汽用途(svp.get蒸汽用途()).set水处设备型(svp.get水处设型()).set水处设运行(pa.get水处模式()).set水处设制单(svp.get水处造单()).set制水能力(svp.get制水能力())
					.set除氧方式(pa.get除氧());
		}
		else if(unproxiedEntity instanceof Amusement) {
			Amusement device = (Amusement) unproxiedEntity;
			AmusementSvp svp= JSON.parseObject(device.getSvp(), AmusementSvp.class);
			AmusementPam pa= JSON.parseObject(device.getPa(), AmusementPam.class);
			snapshot.set设备等级(device.getLevel()).set额定载人(svp.get额载荷()).set倾角坡度(null != device.getGrad() ? device.getGrad().toString() : null).set回转直径(device.getSdia())
					.set座舱高度(svp.get舱高()).set额定速度(null != device.getVl() ? device.getVl().toString() : null).set驱动形式(svp.get驱动式()).set主机功率(F2String(pa.get主功率()))
					.set使用到期时(null != device.getExpire() ? String.valueOf(device.getExpire()) : null).set额定电压(svp.get电压()).set副功率(pa.get副功率())
					.set水滑梯高度(pa.get滑梯高()).set车道轨距(svp.get轨距()).set轨道长度(svp.get轨长()).set轨道高度(svp.get轨高()).set提升高度(F2String(device.getHlf()))
					.set副速度(svp.get副速度()).set车场面积(F2String(svp.get场面积())).set车船数量(pa.get车数()).set滑道种类(svp.get道种类()).set线路高差(svp.get高差())
					.set承载索根数(svp.get道材索根()).set滑索长度(F2String(pa.get平距())).set回收装置(pa.get回收装()).set滑索道数(svp.get索数()).set额定乘客数(St2String(device.getPnum()));
		}
		String strJson = JSON.toJSONString(snapshot);
		report.setSnapshot(strJson);	  //这里改了snapshot可是报告data里面并不会覆盖掉的。若data没有该字段的前端也能直接拷贝进去。
		//子报告分项报告没必要单独设置Snapshot字段，采用和主报告同一个Snapshot数据。同时保留不一样的可能性。继承主报告。
		if(!StringUtils.hasText(report.getData())) report.setData("{}");        //前端允许报告data=null，但是snapshot不允许为空。
		reports.save(report);
		return report;
	}
}
